/*
 *  Copyright (C) 2007,2008 by Filip Brcic <brcha@gna.org>
 *
 *  This file is part of OOMTK (http://launchpad.net/oomtk)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/** \file interrupts.S
 * \brief Assembly interrupt handlers
 *
 * For more info on interrupt and exception handling for Intel and AMD processors, see
 * \ref ia32v2a "Intel's instruction set reference",
 * \ref ia32v3a "Intel's system programming guide",
 * \ref amd64v2 "AMD's system programming guide"
 */

/* Standard assembly macros */
#include <asm.h>

/* Selectors */
#include "Selectors.h"
/* Offsets (autogenerated) */
#include "offsets.h"


	/* Entry for the interrupt without error code */
#define INTENTRY(vecno) \
	.text 			; \
GEXT(intEntry_##vecno)		; \
	cli			; \
	pushl $0		; \
	pushl $vecno		; \
	jmp   EXT(intCommon)

	/* Entry for the interrupt with error code */
#define INTENTRY_EC(vecno, label) \
	.text 			; \
GEXT(intEntry_##vecno)		; \
	cli			; \
	pushl $vecno 		; \
	jmp   EXT(label)
	

	.text
	/* Setup the hardware trap entries (page 5-3 of \ref ia31v3a) 	*/
INTENTRY(0)			/* #DE: Divide error (DIV, IDIV)	*/
INTENTRY(1)			/* #DB: Debug exception			*/
INTENTRY(2)			/* Non-maskable interrupt		*/
INTENTRY(3)			/* #BP: Breakpoint (INT3)		*/
INTENTRY(4)			/* #OF: Overflow (INTO)			*/
INTENTRY(5)			/* #BR: Bounds error (BOUND)		*/
INTENTRY(6)			/* #UD: Invalid opcode (UD2 or invalid)	*/
INTENTRY(7)			/* #NM: Device not available (fpu)	*/
INTENTRY_EC(8, intCommon)	/* #DF: Double fault, returns 0		*/
INTENTRY(9)			/* Coprocessor segment overrun		*/
INTENTRY_EC(10, intCommon)	/* #TS: Invalid TSS			*/
INTENTRY_EC(11, intEC)	/* #NP: Segment not present		*/
INTENTRY_EC(12, intEC)	/* #SS: Stack segment fault		*/
INTENTRY_EC(13, intEC)	/* #GP: General protection		*/
INTENTRY_EC(14, intPgFault)	/* #PF: Page fault			*/
INTENTRY(15)			/* reserved - do not use		*/
INTENTRY(16)			/* #MF: x87 fpu (math fault)		*/
INTENTRY_EC(17, intCommon)	/* #AC: Alignment check, returns 0	*/
INTENTRY(18)			/* #MC: Machine check			*/
INTENTRY(19)			/* #XF: SIMD Floating-point exception	*/
	/* v-- Intel reserved - do not use --v */
INTENTRY(20)
INTENTRY(21)
INTENTRY(22)
INTENTRY(23)
INTENTRY(24)
INTENTRY(25)
INTENTRY(26)
INTENTRY(27)
INTENTRY(28)
INTENTRY(29)
INTENTRY(30)
INTENTRY(31)
	/* ^-- Intel reserved - do not use --^ */

        /* v-- Hardware IRQs --v */
INTENTRY(32)
INTENTRY(33)
INTENTRY(34)
INTENTRY(35)
INTENTRY(36)
INTENTRY(37)
INTENTRY(38)
INTENTRY(39)
INTENTRY(40)
INTENTRY(41)
INTENTRY(42)
INTENTRY(43)
INTENTRY(44)
INTENTRY(45)
INTENTRY(46)
INTENTRY(47)
	/* ^-- Hardware IRQs --^ */
INTENTRY(48)


	/** Common entry for interrupts
	 *
	 * Hardware resets SS & CS (change of GDT entry)
	 * We have to reset the rest of the segment registers
	 */
	.text
	.align ALIGN
GEXT(intCommon)
	pusha	/* In the following order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI */
	movl	%esp, %ebp	/* Move the stack pointer (context) into ebp  */
	/* Check if we are in kernel already... */
	testl	$sel_KernelCS, OFFSET_CONTEXT_CS(%ebp)
	/* If so, skip saving segment registers */
	jz	_inKernel

	/* If not so, save the segment registers into the context (ebp)   */
	mov	%gs, OFFSET_CONTEXT_GS(%ebp)
	mov	%fs, OFFSET_CONTEXT_FS(%ebp)
	mov	%es, OFFSET_CONTEXT_ES(%ebp)
	mov	%ds, OFFSET_CONTEXT_DS(%ebp)

	/* Load kernel data segment */
	mov     $sel_KernelDS, %ax
	mov     %ax, %ds
	mov	%ax, %es
	mov	%ax, %fs
	mov	%ax, %gs

	/* Load kernel stack */
	movl	$kstack_hi, %esp

	/* If kernel was interrupted, routine skips until here */
_inKernel:
	/* Just call interruptHandler(context save area = %ebp) */
	pushl	%ebp
	call	EXT(interruptHandler)

	/* This shouldn't return, so assume some error if this returns */
	call	EXT(halt) 
	
	/** Entry for PageFault exception
	 */
	.text
	.align	ALIGN
GEXT(intPgFault)
	/* Non at the moment... just go to common interrupt entry */
	jmp	intCommon

	/** Entry for error-code returning exceptions
	 */
	.text
	.align	ALIGN
GEXT(intEC)
	/* Nothing to do for now... */
	jmp	intCommon

